<!-- 自定义form表单 案例！！！ -->
<template>
  <div class="mine">
    <z-head text="测试表单组件" leftSvg=""></z-head>
    <z-form v-model="model" :rules="rules" ref="form">
      <z-formItem prop="username">
        <z-input label="用户名" v-model="model.username" :maxlength="15"></z-input>
      </z-formItem>
      <z-formItem prop="password">
        <z-input label="密码" v-model="model.password" :maxlength="18" type="password" showPassword></z-input>
      </z-formItem>
      <z-formItem prop="height">
        <z-input label="身高" v-model="model.height" :maxlength="3" unit="cm"></z-input>
      </z-formItem>
      <z-formItem prop="description">
        <z-input label="富文本" v-model="model.description" type="textarea"></z-input>
      </z-formItem>
      <z-formItem prop="select1Text">
        <z-select label="select下拉框1" :labelWidth="100" v-model="model.select1Text" :columns="selectObj1.list" @confirm="confirm1"></z-select>
      </z-formItem>
      <z-formItem prop="select2Text">
        <z-select label="select下拉框2" :labelWidth="100" v-model="model.select2Text" :columns="selectObj2.list" :allowClick="selectObj2.allowClick" @confirm="confirm2"></z-select>
      </z-formItem>
      <z-formItem prop="select3Text">
        <z-select label="select级联" :labelWidth="100" v-model="model.select3Text" :columns="selectObj3.list" @confirm="confirm3"></z-select>
      </z-formItem>
      <z-formItem>
        <z-select label="select级联回选" :labelWidth="100" v-model="model.select4Text" :columns="selectObj4.list" @confirm="confirm4"></z-select>
      </z-formItem>
      <z-formItem>
        <z-cascader label="cascader级联" :labelWidth="100" v-model="model.cascader1Text" title="请选择地区" :options="cascaderOptions1" @finish="finish1"></z-cascader>
      </z-formItem>
      <z-formItem>
        <z-cascader
          label="cascader级联回选"
          :labelWidth="120"
          v-model="model.cascader2Text"
          title="请选择地区"
          :options="cascaderOptions2"
          :defaultValue="model.cascader2Code"
          @finish="finish2"
        ></z-cascader>
      </z-formItem>
      <z-formItem prop="time1">
        <z-time label="开始时间" v-model="model.time1" type="datetime" @confirm="confirm5"></z-time>
      </z-formItem>
      <z-formItem prop="time2">
        <z-time label="结束时间" v-model="model.time2" type="datetime" @confirm="confirm6"></z-time>
      </z-formItem>
      <z-formItem>
        <z-calendar label="日历单选" v-model="model.calendar1" type="single" :default-date="model.calendar1DefaultDate"></z-calendar>
      </z-formItem>
      <z-formItem>
        <z-calendar label="日历范围" v-model="model.calendar2" type="range"></z-calendar>
      </z-formItem>
      <z-formItem prop="fileList">
        <z-uploader v-model="model.fileList" :before-read="beforeRead" :after-read="afterRead"></z-uploader>
      </z-formItem>
    </z-form>
    <ul class="buttons">
      <li @click="reset">清空</li>
      <li @click="submit">提交</li>
    </ul>
  </div>
</template>

<script setup>
import { ref, reactive } from 'vue'
import { Toast, Notify } from 'vant'
import $day from 'dayjs'
import reg from '@/utils/reg'
import { checkFile } from '@/utils'

const form = ref()
const model = reactive({
  username: '',
  password: '',
  height: '',
  description: '',
  select1Text: '',
  select1Code: '',
  select2Text: '',
  select2Code: '',
  select3Text: '',
  select3Code: null,
  select4Text: '',
  select4Code: null,
  cascader1Text: '',
  cascader1Code: null,
  cascader2Text: '',
  cascader2Code: null,
  time1: $day(),
  time2: $day(),
  calendar1: '2022-05-25',
  calendar1DefaultDate: new Date('2022-05-25'),
  calendar2: null,
  fileList: null
})
const rules = reactive({
  username: [{ required: true, validator: reg('loginName'), trigger: 'blur' }],
  password: [{ required: true, validator: reg('loginPwd'), trigger: 'blur' }],
  height: [{ validator: reg('intNum'), trigger: 'blur' }],
  description: [{ validator: reg('textName'), trigger: 'blur' }],
  select1Text: [{ required: true, message: '请选择', trigger: 'change' }],
  select2Text: [{ required: true, message: '请选择', trigger: 'change' }],
  select3Text: [{ required: true, message: '请选择', trigger: 'change' }],
  time1: [{ required: true, message: '请选择', trigger: 'change' }],
  time2: [{ required: true, message: '请选择', trigger: 'change' }],
  fileList: [{ required: true, message: '请选择', type: 'array', trigger: 'change' }]
})

// 单选框配置项
const selectObj1 = reactive({
  list: [
    { text: '张三', code: '3' },
    { text: '李四', code: '4' },
    { text: '王五', code: '5' }
  ]
})
const selectObj2 = reactive({
  list: [
    { text: '马六', code: '7' },
    { text: '秦七', code: '8' },
    { text: '陈八', code: '9' }
  ],
  allowClick: false
})
const selectObj3 = reactive({
  list: [
    {
      text: '',
      code: '',
      children: [
        {
          text: '浙江',
          code: 1,
          children: [
            {
              text: '杭州',
              code: 11,
              children: [
                { text: '西湖区', code: 111 },
                { text: '余杭区', code: 112 }
              ]
            },
            {
              text: '温州',
              code: 12,
              children: [{ text: '鹿城区', code: 122 }]
            }
          ]
        },
        {
          text: '福建',
          code: 2,
          children: [
            {
              text: '福州',
              code: 22,
              children: [{ text: '鼓楼区', code: 222 }]
            }
          ]
        }
      ]
    }
  ]
})
const selectObj4 = reactive({
  list: [
    {
      text: '',
      code: '',
      children: [
        {
          text: '浙江',
          code: 1,
          children: [
            {
              text: '杭州',
              code: 11,
              children: [
                { text: '西湖区', code: 111, disabled: true },
                { text: '余杭区', code: 112 }
              ]
            },
            {
              text: '温州',
              code: 12,
              children: [
                { text: '鹿城区', code: 122 },
                { text: '瓯海区', code: 123 }
              ]
            }
          ]
        },
        {
          text: '福建',
          code: 2,
          children: [
            {
              text: '福州',
              code: 22,
              children: [
                { text: '鼓楼区', code: 222 },
                { text: '台江区', code: 223 }
              ]
            },
            {
              text: '厦门',
              code: 23,
              children: [
                { text: '思明区', code: 233 },
                { text: '海沧区', code: 234 }
              ]
            }
          ]
        }
      ]
    }
  ],
  defaultIndex: ''
})

// select下拉框与select级联选择器确认函数
function confirm1(data) {
  if (model.select1Code === data.code) return
  model.select1Text = data.text
  model.select1Code = data.code
  model.select2Text = ''
  model.select2Code = ''
  selectObj2.allowClick = !!data.code
}
function confirm2(data) {
  if (model.select2Code === data.code) return
  model.select2Text = data.text
  model.select2Code = data.code
}
function confirm3(data) {
  if (data.code && model.select3Code && model.select3Code.join() === data.code.join()) return
  model.select3Text = data.text
  model.select3Code = data.code
}
function confirm4(data) {
  if (data.code && model.select4Code && model.select4Code.join() === data.code.join()) return
  model.select4Text = data.text
  model.select4Code = data.code
}
// select级联选择器回选
backSelect(selectObj4.list)
model.select4Text = model.select4Text.slice(0, -1)
function backSelect(arr) {
  const codeArr = [2, 23, 233] // 后台给的回选值
  arr.forEach(v => {
    if (v.children) {
      v.children.forEach((item, i) => {
        if (codeArr.indexOf(item.code) !== -1) {
          v.defaultIndex = i
          ;(model.select4Code || (model.select4Code = [])).push(item.code)
          model.select4Text += `${item.text},`
        }
      })
      backSelect(v.children)
    }
  })
}

// 时间确认函数（如果时间与时间不需要做比较，下方函数可省略）
function confirm5(val) {
  if (val && model.time2) {
    if ($day(val).valueOf() > $day(model.time2).valueOf()) {
      Toast('开始时间不能大于结束时间')
      model.time1 = ''
      return
    }
  }
  model.time1 = val
}
function confirm6(val) {
  if (val && model.time1) {
    if ($day(val).valueOf() < $day(model.time1).valueOf()) {
      Toast('开始时间不能大于结束时间')
      model.time2 = ''
      return
    }
  }
  model.time2 = val
}

// cascader级联选择器
const cascaderOptions1 = reactive([
  {
    text: '浙江省',
    value: '330000',
    children: [{ text: '杭州市', value: '330100' }]
  },
  {
    text: '江苏省',
    value: '320000',
    children: [{ text: '南京市', value: '320100' }]
  }
])
const cascaderOptions2 = reactive([
  {
    text: '安徽省',
    value: '330000',
    children: [
      { text: '合肥市', value: '330100' },
      { text: '滁州市', value: '330200' }
    ]
  },
  {
    text: '江苏省',
    value: '320000',
    children: [
      { text: '南京市', value: '320100' },
      { text: '苏州市', value: '320200' }
    ]
  }
])
function finish1({ text, code }) {
  if (model.cascader1Code && code && model.cascader1Code.join() === code.join()) return
  model.cascader1Text = text
  model.cascader1Code = code
}
function finish2({ text, code }) {
  if (model.cascader2Code && code && model.cascader2Code.join() === code.join()) return
  model.cascader2Text = text
  model.cascader2Code = code
}
// cascader级联选择器回选
const codeArr = ['320000', '320200'] // 后台给的code码
model.cascader2Code = codeArr
backCascader(cascaderOptions2, codeArr)
model.cascader2Text = model.cascader2Text.slice(0, -1)
function backCascader(arr, codeArr) {
  arr.forEach(v => {
    if (codeArr.indexOf(v.value) !== -1) {
      model.cascader2Text += `${v.text},`
    }
    if (v.children) backCascader(v.children, codeArr)
  })
}

// 文件读取前的回调函数(常用于限制文件的上传类型及文件大小等)
function beforeRead(file, detail) {
  if (checkFile(file.name) === 'image' || checkFile(file.name) === 'word' || checkFile(file.name) === 'excel') {
    console.log('---上传前的文件---', file, detail)
    return true
  }
  Toast('仅支持 图片/word/excel 的常见格式')
  return false
}

// 上传成功(调用后台文件服务接口，将文件上传至文件服务，更新文件列表)
function afterRead(file, detail) {
  console.log('---上传成功的文件---', file, detail)
  file.status = 'uploading'
  file.message = '上传中...'
  setTimeout(() => {
    const name = file.file.name
    let obj = {}
    switch (checkFile(name)) {
      case 'image':
        obj = { url: 'https://img0.baidu.com/it/u=3976458258,1573032133&fm=253&fmt=auto&app=138&f=PNG?w=977&h=370', isImage: true, type: 'image', name }
        break
      case 'word':
        obj = { url: '', type: 'word', name }
        break
      case 'excel':
        obj = { url: '', type: 'excel', name }
        break
      default:
        break
    }
    ;(model.fileList || (model.fileList = [])).push(obj)
    file.status = 'done'
    file.message = '上传成功'
    Notify({ type: 'success', message: '上传成功' })
  }, 1000)
}

// 表单提交
function submit() {
  form.value.validate(flag => {
    if (!flag) return
    console.log('--提交表单数据--', model)
  })
}
// 表单清空
function reset() {
  form.value.reset()
  model.select1Code = ''
  model.select2Code = ''
  model.select3Code = null
  model.select4Code = null
  model.cascader1Code = null
  model.cascader2Code = null
  model.calendar1DefaultDate = new Date()
  console.log('--提交表单数据--', model)
}
</script>

<style lang="scss" scoped>
.mine {
  height: calc(100% - 50px);
  display: flex;
  flex-direction: column;
  .z-form {
    flex: 1;
    overflow-y: auto;
  }
  .buttons {
    padding: 0 15px;
    @include flex(center, space-between);
    > li {
      height: 40px;
      width: 49%;
      border-radius: 40px;
      @include flex(center, center);
      color: #fff;
      font-size: 16px;
      background-color: $color1;
      margin: 10px 0;
      &:first-child {
        background-color: #b1aa5f;
      }
      &:active {
        transform: scale(1.05);
      }
    }
  }
}
</style>
